package com.zzl.java8.lambda6;

import com.gitee.yongzhuzl.commonutil.util.equals.EqualsUtil;
import com.gitee.yongzhuzl.commonutil.util.math.MathUtil;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 参考链接：https://www.jianshu.com/p/32fd655cc2c2
 * description: 精通并会熟练使用Java8-Lambda表达式、学会使用Stream API
 * @author 朱正磊
 * @date 2020-04-22 13:40:48
 */
public class MasterLambda {

    /**
     * 传统写法和Stream API写法对比
     */
    @Test
    void test1() {
        // 构造数据
        List<Employee> list = Arrays.asList(
                new Employee(1, "zhansgan", 21, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu", 23, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "tianqi", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "sunjiu", 27, 700D)
        );

        // 返回薪资大于500的员工列表，java8以前是这样做的
        List<Employee> newList1 = new ArrayList<>();
        for (Employee employee : list) {
            if (EqualsUtil.isEquals(MathUtil.compare(employee.getSalary().toString(), "500"), 1)) {
                newList1.add(employee);
            }
        }
        // 600.0
        // 700.0
        newList1.forEach(e -> System.out.println(e.getSalary()));

        // 使用Stream API，代码可以这样
        // 600.0
        // 700.0
        list.stream().filter(e1 -> EqualsUtil.isEquals(MathUtil.compare(e1.getSalary().toString(), "500"), 1))
                .collect(Collectors.toList()).forEach(e2 -> System.out.println(e2.getSalary()));
    }

    /**
     * 映射(map/flatMap)
     */
    @Test
    void test2() {
        // map——接收 Lambda，将元素转换成其他形式或提取信息。接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
        // map操作会将流里的每个元素按mapper转换后的结果添加到一个新流中.
        List<String> list = Arrays.asList("1,2", "3,4");
        // 每次mapper操作返回一个数组，将每个数组添加到新流中，最终生成Stream<String[]>
        Stream<String[]> stream = list.stream().map(e -> e.split(","));
        // 每次mapper操作返回一个流Stream<String>，将每个流添加到新流中，最终生成Stream<Stream<String>>
        Stream<Stream<String>> streamStream = list.stream().map(e -> Arrays.stream(e.split(",")));

        // flatMap——接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
        Stream<String> stringStream = list.stream().flatMap(e -> Arrays.stream(e.split(",")));
    }

    /**
     * 排序(sort)
     * sorted() ——自然排序(根据流中元素实现的Comparable接口的compareTo()方法来排序的)
     * sorted(Comparator com) ——定制排序(根据特定的比较器来排序)
     */
    @Test
    void test3() {
        List<Integer> list = Arrays.asList(1, 3, 9, 0, 4, 8, 2, 7, 6, 5);
        // 0123456789
        list.stream().sorted().forEach(System.out::print);

        List<Employee> list2 = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu", 27, 700D)
        );
        // lisi,22
        // wangwu,22
        // zhansgan,22
        // zhaoliu,24
        // wangwu,25
        // zhuba,26
        // wangwu,27
        list2.stream().sorted((x, y) -> {
            if (EqualsUtil.isEquals(x.getAge(), y.getAge())) {
                return x.getName().compareTo(y.getName());
            } else {
                return x.getAge().compareTo(y.getAge());
            }
        }).forEach(e -> System.out.println(e.getName() + "," + e.getAge() + " "));
    }

    /**
     * 筛选与切片
     * filter: 接受Lambda,从流中排除某些元素
     */
    @Test
    void test4() {
        List<Employee> list = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu1", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu2", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu3", 27, 700D)
        );

        // 筛选出姓名为wangwu的员工
        // Employee{id=3, name='wangwu1', age=22, salary=300.0}
        // Employee{id=5, name='wangwu2', age=25, salary=500.0}
        // Employee{id=7, name='wangwu3', age=27, salary=700.0}
        list.stream().filter(e -> e.getName().startsWith("wangwu")).forEach(System.out::println);
    }

    /**
     * limit(n): 返回流中前n个元素
     */
    @Test
    void test5() {
        List<Employee> list = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu1", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu2", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu3", 27, 700D)
        );

        // 获取列表前3个员工
        // Employee{id=1, name='zhansgan', age=22, salary=100.0}
        // Employee{id=2, name='lisi', age=22, salary=200.0}
        // Employee{id=3, name='wangwu1', age=22, salary=300.0}
        list.stream().limit(3).forEach(System.out::println);
    }

    /**
     * skip(n): 跳过流中前n个元素
     */
    @Test
    void test6() {
        List<Employee> list = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu1", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu2", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu3", 27, 700D)
        );

        // 去掉前3个员工
        // Employee{id=4, name='zhaoliu', age=24, salary=400.0}
        // Employee{id=5, name='wangwu2', age=25, salary=500.0}
        // Employee{id=6, name='zhuba', age=26, salary=600.0}
        // Employee{id=7, name='wangwu3', age=27, salary=700.0}
        list.stream().skip(3).forEach(System.out::println);
    }

    /**
     * distinct: 去掉流中重复元素(通过hashCode和equles方法判断是否为相同对象)
     */
    @Test
    void test7() {
        List<Integer> list = Arrays.asList(1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6);
        // 去掉重复元素
        // 123456
        list.stream().distinct().forEach(System.out::print);
    }

    /**
     * allMatch——检查是否匹配所有元素
     */
    @Test
    void test8() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 检查所有元素是否都小于10
        boolean flag = list.stream().allMatch(e -> e < 10);
        // true
        System.out.println(flag);
    }

    /**
     * anyMatch——检查是否至少匹配一个元素
     */
    @Test
    void test9() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 检查是否至少有一个元素等于5
        boolean flag = list.stream().anyMatch(e -> e == 5);
        // true
        System.out.println(flag);
    }

    /**
     * noneMatch——检查是否没有匹配的元素
     */
    @Test
    void test10() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 检查是否没有一个元素等于7
        boolean flag = list.stream().noneMatch(e -> e == 7);
        // true
        System.out.println(flag);
    }

    /**
     * findFirst——返回第一个元素
     */
    @Test
    void test11() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 返回集合中的第一个元素
        Optional<Integer> optional = list.stream().findFirst();
        Integer result = optional.orElse(-1);
        // 1
        System.out.println(result);
    }

    /**
     * findAny——返回当前流中的任意元素
     */
    @Test
    void test12() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Optional<Integer> optional = list.stream().findAny();
        Integer result = optional.orElse(-1);
        // 1
        System.out.println(result);
    }

    /**
     * count——返回流中元素的总个数
     */
    @Test
    void test13() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        // int size = list.size();
        long count = list.stream().count();
        // 6
        System.out.println(count);
    }

    /**
     * max——返回流中最大值
     */
    @Test
    void test14() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 6
        System.out.println(max.orElse(-1));
    }

    /**
     * min——返回流中最小值
     */
    @Test
    void test15() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Optional<Integer> min = list.stream().min(Integer::compareTo);
        // 1
        System.out.println(min.orElse(-1));
    }

    /**
     * 规约(reduce)
     */
    @Test
    void test16() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        // 这里reduce操作中,
        // 起始值为0,
        // 第一次x为0,
        // list中第一个元素1为y
        // 经行(x+y)操作,
        // 然后又把(x+y)的值作为x,
        // list中第二个元素2作为y,
        // 依次累加.最终得到一个sum值
        // 55
        System.out.println(sum);

        List<Employee> list2 = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu1", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu2", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu3", 27, 700D)
        );
        Optional<Double> optional = list2.stream().map(Employee::getSalary).reduce(Double::sum);
        // 这个地方由于没有初始值,计算结果可能为空(列表为空的情况),所以就把计算结果封装到Optional中避免空指针异常
        // 2800.0
        System.out.println(optional.orElse(null));
    }

    /**
     * 收集(collect)
     * collect——将流转换为其他形式。接收一个 Collector接口的实现，用于给Stream中元素做汇总的方法
     */
    @Test
    void test17() {
        List<Employee> list = Arrays.asList(
                new Employee(1, "zhansgan", 22, 100D),
                new Employee(2, "lisi", 22, 200D),
                new Employee(3, "wangwu1", 22, 300D),
                new Employee(4, "zhaoliu", 24, 400D),
                new Employee(5, "wangwu2", 25, 500D),
                new Employee(6, "zhuba", 26, 600D),
                new Employee(7, "wangwu3", 27, 700D)
        );
        // 收集员工姓名到List集合
        List<String> nameList = list.stream().map(Employee::getName).collect(Collectors.toList());
        // [zhansgan, lisi, wangwu1, zhaoliu, wangwu2, zhuba, wangwu3]
        System.out.println(nameList);

        // 收集员工姓名到Set集合
        Set<String> nameSet = list.stream().map(Employee::getName).collect(Collectors.toSet());
        // [lisi, zhaoliu, wangwu3, wangwu1, wangwu2, zhansgan, zhuba]
        System.out.println(nameSet);

        Set<String> nameSet2 = list.stream().map(Employee::getName).collect(Collectors.toCollection(LinkedHashSet::new));
        // [zhansgan, lisi, wangwu1, zhaoliu, wangwu2, zhuba, wangwu3]
        System.out.println(nameSet2);
    }

    /**
     * 分组
     * groupingBy: 根据指定的元素对流中数据进行分组
     */
    @Test
    void test18() {
        // 按照员工姓氏分组，这里不考虑复姓
        List<Employee> list = Arrays.asList(
                new Employee(1, "张三", 22, 100D),
                new Employee(2, "李四", 22, 200D),
                new Employee(3, "王五", 22, 300D),
                new Employee(4, "赵六", 24, 400D),
                new Employee(5, "王七", 25, 500D),
                new Employee(6, "朱八", 26, 600D),
                new Employee(7, "王九", 27, 700D)
        );

        Map<String, List<Employee>> collect = list.stream()
                .collect(Collectors.groupingBy(e -> String.valueOf(e.getName().charAt(0))));
        // 张---[Employee{id=1, name='张三', age=22, salary=100.0}]
        // 朱---[Employee{id=6, name='朱八', age=26, salary=600.0}]
        // 赵---[Employee{id=4, name='赵六', age=24, salary=400.0}]
        // 王---[Employee{id=3, name='王五', age=22, salary=300.0}, Employee{id=5, name='王七', age=25, salary=500.0}, Employee{id=7, name='王九', age=27, salary=700.0}]
        // 李---[Employee{id=2, name='李四', age=22, salary=200.0}]
        collect.forEach((x, y) -> System.out.println(x + "---" + y));
    }

    /**
     * 分区
     * partitioningBy: 按照给定条件对流中元素进行分区
     */
    @Test
    void test19() {
        // 将员工以薪资400为界限分区
        List<Employee> list = Arrays.asList(
                new Employee(1, "张三", 22, 100D),
                new Employee(2, "李四", 22, 200D),
                new Employee(3, "王五", 22, 300D),
                new Employee(4, "赵六", 24, 400D),
                new Employee(5, "王七", 25, 500D),
                new Employee(6, "朱八", 26, 600D),
                new Employee(7, "王九", 27, 700D)
        );
        Map<Boolean, List<Employee>> collect = list.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() >= 400));
        // false---[Employee{id=1, name='张三', age=22, salary=100.0}, Employee{id=2, name='李四', age=22, salary=200.0}, Employee{id=3, name='王五', age=22, salary=300.0}]
        // true---[Employee{id=4, name='赵六', age=24, salary=400.0}, Employee{id=5, name='王七', age=25, salary=500.0}, Employee{id=6, name='朱八', age=26, salary=600.0}, Employee{id=7, name='王九', age=27, salary=700.0}]
        collect.forEach((x, y) -> System.out.println(x + "---" + y));
    }

}
